#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <gcrypt.h>
#include <netinet/in.h>

#include <ev.h>
#include "ebb.h"

#define IS_ESCAPED(c) ((c) == '\'' || (c) == '\\' || (c) == '\r' || (c) == '\n')

#define SIN struct sockaddr_in
#define zalloc(b) zealloc(b,0)

#if 0
#define DEBUG(fmt,...) {}
#define zfree(x) do { if (x) { free(x); x = NULL; } } while (0);
#define struct_off(x,y) ((void *) (((char *) (x)) + ((unsigned)(y))))
#define zealloc(b,n) do { b = malloc(sizeof(*b) + (n)); memset(b, 0, sizeof(*b) + (n)); } while(0);
#else
#define DEBUG(fmt...) { fprintf(stderr, "%s:%d @ %s(): \e[1;37m", __FILE__, __LINE__, __func__); fprintf(stderr, fmt); fprintf(stderr,"\e[0m\n"); }
#define zealloc(b,n) do { b = malloc(sizeof(*b) + (n)); memset(b, 0, sizeof(*b) + (n)); DEBUG("zealloc(%p)=%d", b, sizeof(*b) + (n)); } while(0);
#define struct_off(x,y) ((void *) (((char *) (x)) + ((unsigned)(y)))); DEBUG("struct_off(%p,%d)", x, y);
#define zfree(x) do { printf("zfree(%p)\n", x); if (x) { free(x); x = NULL; } } while (0);
#endif

#define str(x) #x

/* request line buffer */
#define RBUF 4096

/* irc-line buffer */
#define IBUF 512

/* all lenghts are for hexstring representation */
#define ID_LEN 6	/* ID (for fast lookup in client table) */
#define K_LEN 14	/* KEY sessdion key */

#define IDK_LEN (ID_LEN + K_LEN)

/* this is allocated per each irc packet received. data[] are guaranteed to start at cr/lf boundary */
struct	rentry {
	struct	rentry *next; /* next-with-higher seqr (tail */
	char	data[0]; /* chunklen\r\nirc_update(1234567,0,0,"recvbuffer"); */
};
struct client_info_t;
struct request_info_t;
struct auth_info_t;
typedef struct client_info_t client_info;
typedef struct request_info_t request_info;
typedef struct auth_info_t auth_info;

/* per-irc client info */
struct	client_info_t {
	char	idk[IDK_LEN+1];
	char	ibuf[IBUF];
	int	ibuflen;
	unsigned rqend; /* highest seq, rq[rqend] always invalid! */
	unsigned rqpos; /* our lowest seq */
	struct	rentry **rq; /* the table starting at rqpos */
	int	active:1; /* 1 - reqs is valid, 0 - next_free is valid */
	int	connected:1; /* connection is valid */
	ebb_connection c; /* non-NULL if connecting/ed */
	request_info *pending; /* pending requests for write */
	union {
		/* pending requests on this client */
		request_info *reqs;
		client_info *next_free; /* client free list */
	};
};



struct request_info_t {
	struct	request_info_t *next; /* next in list for this client */
	client_info *c;
	ebb_connection *conn;
	char rbuf[RBUF+1];
	int rlen; /* length of path received */
	unsigned rseq; /* requested seq */
	struct rentry *rq;
};

struct	auth_info_t {
	char hostname[256]; /* resolved hostname or 0 */
	char id[128]; /* raw ident response looked up */

	ebb_connection ident;
	void *cb;	/* user callback when done */
	void *data;	/* cb data */
	struct dns_query *q; /* call dns_cancel on the when in forced exit */
	int dns_state; /* 0 - reverse lookup, 1 - forward lookup, 2 - done */
	int ident_state; /* 0 - connecting, 1 - got reply */
	ev_timer timeout;    /* private */
	char wbuf[64];

#ifdef INET6
	uint32_t ip[4];
	int ipv6; /* if looking up v6 address */
#else
	uint32_t ip[1];
#endif
};




void url_decode(char *buf);
void escape_buffer(char *dst, char *src, int len);
int count_escapes(char *s, int len);
void	hex_random_bytes(char *dst, int n);
ebb_server	*http_init(struct ev_loop *loop, int argc, char *argv[]);
void	auth_init();
auth_info *auth_stop(auth_info *ai);
auth_info *auth_start(struct ev_loop *loop, struct SOCKADDR_IN *server, struct SOCKADDR_IN *client, int timeout);


